package com.example.service;

import com.example.model.Device;
import com.example.model.DeviceType;
import lombok.extern.slf4j.Slf4j;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Slf4j
public class DeviceService {
    private final Ignite ignite;
    private Tenant currentTenant;

    public DeviceService(Ignite ignite) {
        this.ignite = ignite;
    }

    public void setTenant(Tenant tenant) {
        this.currentTenant = tenant;
    }

    public Tenant getCurrentTenant() {
        return currentTenant;
    }

    private String getDeviceCacheName() {
        return "SQL_" + currentTenant.name() + "_DEVICE";
    }

    private String getDeviceTypeCacheName() {
        return "SQL_" + currentTenant.name() + "_DEVICE_TYPE";
    }

    private String getSchemaPrefix() {
        return currentTenant.getSchema() + ".";
    }

    public List<Device> findRecentDevices() {
        checkTenant();
        List<Device> devices = new ArrayList<>();

        String sql = "SELECT * FROM " /* + getSchemaPrefix() */ + "DEVICE " +
                "WHERE createtime >= ? " +
                "ORDER BY createtime DESC";

        try (IgniteCache<Long, Device> cache = ignite.cache(getDeviceCacheName())) {
            SqlFieldsQuery query = new SqlFieldsQuery(sql)
                    .setArgs(getDateMinusSixMonths());

            try (QueryCursor<List<?>> cursor = cache.query(query)) {
                for (List<?> row : cursor) {
                    devices.add(mapRowToDevice(row));
                }
            }
        } catch (Exception e) {
            log.error("Error finding recent devices for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to query devices", e);
        }

        return devices;
    }

    public List<DeviceType> findAllDeviceTypes() {
        checkTenant();
        List<DeviceType> deviceTypes = new ArrayList<>();

        String sql = "SELECT * FROM " /*+ getSchemaPrefix() */+ "DEVICETYPE";

        try (IgniteCache<Long, DeviceType> cache = ignite.cache(getDeviceTypeCacheName())) {
            SqlFieldsQuery query = new SqlFieldsQuery(sql);

            try (QueryCursor<List<?>> cursor = cache.query(query)) {
                for (List<?> row : cursor) {
                    deviceTypes.add(mapRowToDeviceType(row));
                }
            }
        } catch (Exception e) {
            log.error("Error finding device types for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to query device types", e);
        }

        return deviceTypes;
    }

    public Device findDeviceById(Long id) {
        checkTenant();
        try (IgniteCache<Long, Device> cache = ignite.cache(getDeviceCacheName())) {
            return cache.get(id);
        }
    }

    public DeviceType findDeviceTypeById(Long id) {
        checkTenant();
        try (IgniteCache<Long, DeviceType> cache = ignite.cache(getDeviceTypeCacheName())) {
            return cache.get(id);
        }
    }

    public List<Device> findDevicesByType(Long typeId) {
        checkTenant();
        List<Device> devices = new ArrayList<>();

        String sql = "SELECT * FROM " /*+ getSchemaPrefix() */+ "DEVICE " +
                "WHERE devicetypeid = ?";

        try (IgniteCache<Long, Device> cache = ignite.cache(getDeviceCacheName())) {
            SqlFieldsQuery query = new SqlFieldsQuery(sql)
                    .setArgs(typeId);

            try (QueryCursor<List<?>> cursor = cache.query(query)) {
                for (List<?> row : cursor) {
                    devices.add(mapRowToDevice(row));
                }
            }
        } catch (Exception e) {
            log.error("Error finding devices by type: " + typeId + " for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to query devices by type", e);
        }

        return devices;
    }

    public void saveDevice(Device device) {
        checkTenant();
        try (IgniteCache<Long, Device> cache = ignite.cache(getDeviceCacheName())) {
            cache.put(device.getId(), device);
        } catch (Exception e) {
            log.error("Error saving device: " + device.getId() + " for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to save device", e);
        }
    }

    public void saveDeviceType(DeviceType deviceType) {
        checkTenant();
        try (IgniteCache<Long, DeviceType> cache = ignite.cache(getDeviceTypeCacheName())) {
            cache.put(deviceType.getId(), deviceType);
        } catch (Exception e) {
            log.error("Error saving device type: " + deviceType.getId() + " for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to save device type", e);
        }
    }

    public void deleteDevice(Long id) {
        checkTenant();
        try (IgniteCache<Long, Device> cache = ignite.cache(getDeviceCacheName())) {
            cache.remove(id);
        } catch (Exception e) {
            log.error("Error deleting device: " + id + " for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to delete device", e);
        }
    }

    public void deleteDeviceType(Long id) {
        checkTenant();
        try (IgniteCache<Long, DeviceType> cache = ignite.cache(getDeviceTypeCacheName())) {
            cache.remove(id);
        } catch (Exception e) {
            log.error("Error deleting device type: " + id + " for tenant: " + currentTenant, e);
            throw new RuntimeException("Failed to delete device type", e);
        }
    }

    private Date getDateMinusSixMonths() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MONTH, -6);
        return cal.getTime();
    }

    private void checkTenant() {
        if (currentTenant == null) {
            throw new IllegalStateException("Tenant not set");
        }
    }

    private Device mapRowToDevice(List<?> row) {
        Device device = new Device();
        int i = 0;
        device.setId((Long) row.get(i++));
        device.setSerialNumber((String) row.get(i++));
        device.setDeviceTypeId((Long) row.get(i++));
        device.setName((String) row.get(i++));
        device.setActive((Boolean) row.get(i++));
        device.setCreateTime((Date) row.get(i));
        return device;
    }

    private DeviceType mapRowToDeviceType(List<?> row) {
        DeviceType deviceType = new DeviceType();
        int i = 0;
        deviceType.setId((Long) row.get(i++));
        deviceType.setName((String) row.get(i++));
        deviceType.setDescription((String) row.get(i++));
        deviceType.setActive((Boolean) row.get(i));
        return deviceType;
    }
}
